package com.tianji.learning.task;

import com.tianji.common.utils.CollUtils;
import com.tianji.learning.constants.RedisConstants;
import com.tianji.learning.domain.po.PointsBoard;
import com.tianji.learning.domain.po.PointsBoardSeason;
import com.tianji.learning.service.IPointsBoardSeasonService;
import com.tianji.learning.service.IPointsBoardService;
import com.tianji.learning.utils.TableInfoContext;
import com.xxl.job.core.context.XxlJobHelper;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;

import static com.tianji.learning.constants.LearningConstants.POINTS_BOARD_TABLE_PREFIX;

@Slf4j
@Component
@RequiredArgsConstructor
public class PointsBoardPersistentHandler {

    private final IPointsBoardSeasonService pointsBoardSeasonService;

    private final IPointsBoardService pointsBoardService;

    private final StringRedisTemplate redisTemplate;

    /**
     * 创建上赛季（上个月）榜单表
     */
//    @Scheduled(cron = "0 13 15 13 1 ?")// 每月13号 15点09执行
//    @Scheduled(cron = "0 0 3 1 * ?") // 每月1号 凌晨3点执行 //单机版 定时任务调度
    @XxlJob("createTableJob")
    public void createPointsBoardTableOfLastSeason(){
        //1.获取上月时间
        LocalDate time = LocalDate.now().minusMonths(1);//获取当前时间的上月时间
        //2.查询赛季id  条件 begin_time <= time && end_time >= time
        PointsBoardSeason one = pointsBoardSeasonService.lambdaQuery()
                .le(PointsBoardSeason::getBeginTime, time)
                .ge(PointsBoardSeason::getEndTime, time)
                .one();
        log.debug("上赛季信息 ：{}", one);
        if (one == null){
            log.error("上 seasons 不存在，无法创建上 seasons");
            return;
        }
        //3.创建表
        pointsBoardSeasonService.createPointsBoardLatestTable(one.getId());
    }

    /**
     * 持久化上赛季（上个月的）排行榜数据 到db中
     */
    @XxlJob("savePointsBoard2DB")
    public void savePointsBoard2DB(){
        log.debug("开始持久化上赛季排行榜数据到 db 中");
        //1.获取上个月 当前的时间点
        LocalDate time = LocalDate.now().minusMonths(1);
        //2.查询赛季表points_board_season 获取上赛季信息
        PointsBoardSeason one = pointsBoardSeasonService.lambdaQuery()
                .le(PointsBoardSeason::getBeginTime, time)
                .ge(PointsBoardSeason::getEndTime, time)
                .one();
        log.debug("上赛季信息 ：{}", one);
        if (one == null){
            log.error("上赛季不存在");
            return;
        }
        //3.计算动态表名 并存入ThreadLocal
        String tableName = POINTS_BOARD_TABLE_PREFIX + one.getId();
        log.debug("动态表名：{}", tableName);
        TableInfoContext.setInfo(tableName);
        //4.分页获取redis上赛季积分排行榜数据
        String format = time.format(DateTimeFormatter.ofPattern("yyyyMM"));
        String key = RedisConstants.POINTS_BOARD_KET_PREFIX + format;

        int shardIndex = XxlJobHelper.getShardIndex(); //当前分片索引 从0开始
        int shardTotal = XxlJobHelper.getShardTotal(); //总分片数

        int pageNo = shardIndex + 1; // 页码
        int pageSize = 1000;//todo
        while (true){
            log.debug("当前分片索引：{}，当前分片总数：{}", shardIndex,shardTotal);
            log.debug("处理 第：{} 页数据", pageNo);
            List<PointsBoard> pointsBoardList = pointsBoardService.queryCurrentBoard(key, pageNo, pageSize);//分页查询
            if (CollUtils.isEmpty(pointsBoardList)){
                break; //跳出循环 进行下一步
            }
            pageNo += shardTotal;
            //5.持久化到db相应的赛季表中
            for (PointsBoard board : pointsBoardList) {
                board.setId(Long.valueOf(board.getRank()));
                board.setRank(null);
            }
            pointsBoardService.saveBatch(pointsBoardList);
        }
        //6.清除ThreadLocal中的数据
        TableInfoContext.remove();
    }

    /**
     * 清理Redis中的上赛季的榜单数据，避免过多的内存占用
     */
    @XxlJob("clearPointsBoardFromRedis")
    public void clearPointsBoardFromRedis(){
        // 1.获取上月时间
        LocalDateTime time = LocalDateTime.now().minusMonths(1);
        // 2.计算key
        String format = time.format(DateTimeFormatter.ofPattern("yyyyMM"));
        String key = RedisConstants.POINTS_BOARD_KET_PREFIX + format;
        // 3.删除
        redisTemplate.unlink(key);
    }
}
